package robots.behaviour;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;

import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;

public class PacmanFinder implements Strategy {
	
	private Coordinate currentCoordinate;
	private Coordinate pacmanCoordinate;
	private ArrayList<Direction> path = new ArrayList<Direction>();
	private Maze maze;
	private boolean pacmanCaught = false;
	public Maze collaborativeDiffusionMaze;
	public PacmanFinder(Maze maze){
		this.maze=maze;
		collaborativeDiffusionMaze = new Maze(10);
	}
	@Override
	public Direction nextDirection(Location loc, Maze[] mazes, Coordinate[] ghosts) {
		if(pacmanCoordinate==null) return null;
		currentCoordinate=maze.getCoordinateOfLocation(loc);
		fillCollaborativeDiffusionMaze(mazes,ghosts,currentCoordinate);
		TreeSet<ValuedDirection> options = new TreeSet<ValuedDirection>();
		int valCURRENT = collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y, Direction.NORTH, true);
		int valNORTH = collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y+1, Direction.NORTH, true);
		int valEAST = collaborativeDiffusionMaze.read(currentCoordinate.x+1, currentCoordinate.y, Direction.NORTH, true);
		int valSOUTH = collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y-1, Direction.NORTH, true);
		int valWEST = collaborativeDiffusionMaze.read(currentCoordinate.x-1, currentCoordinate.y, Direction.NORTH, true);
		options.add(new ValuedDirection(null,valCURRENT)); //CURRENT
		
		//check NORTH
		int wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.NORTH, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.NORTH, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.NORTH,valNORTH)); //NORTH
		
		//check EAST
		wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.EAST, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.EAST, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.EAST,valEAST)); //EAST
		
		//check SOUTH
		wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.SOUTH, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.SOUTH, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.SOUTH,valSOUTH)); //SOUTH
		
		//check WEST
		wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.WEST, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.WEST, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.WEST,valWEST)); //WEST
		
		return options.last().d;
	}
	
	private void fillCollaborativeDiffusionMaze(Maze[] mazes, Coordinate[] ghosts, Coordinate self) {
		Vector<Coordinate> passed = new Vector<Coordinate>();
		Vector<Coordinate> active = new Vector<Coordinate>();
		passed.add(self);
		for(int i=0; i<ghosts.length; i++) {
			passed.add(ghosts[i]);
			collaborativeDiffusionMaze.write(ghosts[i].x, ghosts[i].y, Direction.NORTH, (char)0, true);
		}
		active.add(pacmanCoordinate);
		char currentHeuristicValue = 0;
		while(active.size()>0) { //as long as there might be longer paths
			Vector<Coordinate> temp = new Vector<Coordinate>();
			for(Coordinate c : active) {
				passed.add(c);
				collaborativeDiffusionMaze.write(c.x, c.y, Direction.NORTH, currentHeuristicValue, true);
				//NORTH
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.NORTH,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x,c.y+1);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//EAST
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.EAST,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x+1,c.y);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//SOUTH
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.SOUTH,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x,c.y-1);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//WEST
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.WEST,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x-1,c.y);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				
			}
			currentHeuristicValue--;
			active = temp; //prepare for the next iteration of the while loop
		}
		currentHeuristicValue++;
		if(currentHeuristicValue==0) pacmanCaught = true;
		else pacmanCaught = false;
	}
	
	@Override
	public boolean exploreEverything() {
		// TODO Auto-generated method stub
		return false;
	}
	
	private class ValuedDirection implements Comparable<ValuedDirection> {
		public Direction d;
		public int val;
		public ValuedDirection(Direction d, int valCURRENT) {
			this.d = d;
			this.val = valCURRENT;
		}
		@Override
		public int compareTo(ValuedDirection o) {
			return (val<o.val)?-1:((val>o.val)?1:0);
		}
	}

	@Override
	public void setCoordinatePacman(Coordinate coordinate) {
		this.pacmanCoordinate = coordinate;
	}
	
	public boolean pacmanCaught() {
		return pacmanCaught;
	}
	
	@Override
	public String toString(){
		return "PacmanFinder";
	}
	@Override
	public void unsetPacman() {
		pacmanCaught=false;
		this.setCoordinatePacman(null);
	}
	
}
